﻿using System;
using System.Collections.Generic;

namespace tuimg.element
{
    using tuimg.helpers;
    using tuimg.structs;

    public class TuimgPath
    {
        internal int height;
        internal int width;
        internal struct_path[] paths;
        internal int point = 0;
        internal int length = 0;
        internal struct_path_viewbox viewbox;
        internal struct_path_point prepoint;
        internal struct_path_point startpoint;
        public TuimgPath(int length) 
        {
            paths = new struct_path[length+1];
            prepoint = new struct_path_point { x = 0, y = 0 };
        }

        public void SetViewBox(int minX, int minY, int maxX, int maxY)
        {
            //viewbox.x_min = minX; viewbox.y_min = minY;
            //viewbox.x_max = maxX; viewbox.y_max = maxY;
            //viewbox._x = minX + maxX; viewbox._y = minY + maxY;
            //width = maxX-minX+2; height = maxY-minY+2;
        }

        public int Width { get { return width; } }
        public int Height { get { return height; } }
        internal void SetViewBox(struct_ttf_glyf_head glyf)
        {
            SetViewBox(glyf.xMin, glyf.yMin, glyf.xMax, glyf.yMax);
        }

        public int Length { get { return length; } }
        public int Position { get { return point; }  set { if (point>=0) point = value; } }
        public int PathCount { get; private set; } = 0;
        internal void update_viewbox(struct_path_point p)
        {
            if (width <= 0)
            {
                viewbox.x_min = p.x; viewbox.y_min = p.y;
                viewbox.x_max = p.x; viewbox.y_max = p.y;
                viewbox._x = viewbox.x_min + viewbox.x_max; viewbox._y = viewbox.y_min + viewbox.y_max;
                width = 2; height = 2;
            }
            else
            {
                if (p.x < viewbox.x_min) viewbox.x_min = p.x;
                else if (p.x > viewbox.x_max) viewbox.x_max = p.x;
                if (p.y < viewbox.y_min) viewbox.y_min= p.y;
                else if (p.y > viewbox.y_max) viewbox.y_max= p.y;
            }
            viewbox._x = viewbox.x_min + viewbox.x_max; viewbox._y = viewbox.y_min + viewbox.y_max;
            width = viewbox.x_max -viewbox.x_min +1; height = viewbox.y_max - viewbox.y_min;
        }
        internal void add_path(struct_path path)
        {
            paths[Position++] = path;
            length++;
            switch(path.method)
            {
                case enum_path_method.Line:
                case enum_path_method.Move:
                    update_viewbox(path.p1);
                    break;
                case enum_path_method.Quadratic_Bezier_Curve:
                    update_viewbox(path.p2);
                    break;
            }
        }
        public void MovePoint(int x, int y)
        {
            PathCount++;

            if (length > 0 && startpoint != prepoint)
            {
                add_path(new struct_path
                {
                    method = enum_path_method.Line,
                    p0 = prepoint,
                    p1 = startpoint
                });
            }
            startpoint = prepoint = new struct_path_point { x = x, y = y };
            add_path(new struct_path
            {
                method = enum_path_method.Move,
                p1 = prepoint
            });
        }
        public void End()
        {
            if (prepoint == startpoint) return;

            add_path(new struct_path
            {
                method = enum_path_method.Line,
                p0 = prepoint,
                p1 = startpoint
            });
            prepoint = startpoint;
        }
        public void Line(int x, int y)
        {
            var p = new struct_path_point { x = x, y = y };
            if (p == prepoint) return;
            add_path(new struct_path
            {
                method = enum_path_method.Line,
                p0 = prepoint,
                p1 = new struct_path_point { x = x, y = y }
            });
            prepoint = paths[Position-1].p1;
        }

        public void Quadratic_Bezier_Curve(int x1, int y1, int x2, int y2)
        {
            add_path(new struct_path
            {
                method = enum_path_method.Quadratic_Bezier_Curve,
                p0 = prepoint,
                p1 = new struct_path_point { x = x1, y = y1 },
                p2 = new struct_path_point { x = x2, y = y2 }
            });
            prepoint = paths[Position-1].p2;
        }

        public void Flip_vertical()
        {
            for (var i = 0; i < length; i++)
            {
                switch (paths[i].method)
                {
                    case enum_path_method.Move:
                        paths[i].p1.Flip_vertical(viewbox);
                        break;
                    case enum_path_method.Quadratic_Bezier_Curve:
                        paths[i].p0.Flip_vertical(viewbox);
                        paths[i].p1.Flip_vertical(viewbox);
                        paths[i].p2.Flip_vertical(viewbox);
                        break;
                    default:
                        paths[i].p0.Flip_vertical(viewbox);
                        paths[i].p1.Flip_vertical(viewbox);
                        break;
                }
            }
        }
        public void Flip_horizontal()
        {
            for (var i = 0; i < length; i++)
            {
                switch (paths[i].method)
                {
                    case enum_path_method.Move:
                        paths[i].p1.Flip_horizontal(viewbox);
                        break;
                    case enum_path_method.Quadratic_Bezier_Curve:
                        paths[i].p0.Flip_horizontal(viewbox);
                        paths[i].p1.Flip_horizontal(viewbox);
                        paths[i].p2.Flip_horizontal(viewbox);
                        break;
                    default:
                        paths[i].p0.Flip_horizontal(viewbox);
                        paths[i].p1.Flip_horizontal(viewbox);
                        break;
                }
            }
        }
        public void Scale(decimal per)
        {
            for(var i =0; i < length; i++) paths[i].Scale(per);
            SetViewBox(helper.round_down((double)(viewbox.x_min * (per))), helper.round_down((double)(viewbox.y_min * (per))), helper.round_up((double)(viewbox.x_max * (per))), helper.round_up((double)(viewbox.y_max * (per))));
        }
        public void Move(int x, int y)
        {
            for (var i = 0; i < length; i++) paths[i].Move(x, y);
            SetViewBox(viewbox.x_min+x, viewbox.y_min+y, viewbox.x_max +x, viewbox.y_max+y);
        }
        public TuimgGrid ToGrid_ScaleHeight(int height)
        {
            var scale_per = height / (decimal)Height;
            var width = (int)(scale_per * Width);

            var grid = new TuimgGrid(width+4, height+4);
            grid.DrawPath(2,2, this, scale_per);
            grid = grid.CopyTrim();
            return grid;

        }
        public TuimgGrid ToGrid()
        {
            var grid = new TuimgGrid((Width | 0b111), (Height | 0b111));
            grid.DrawPath(2, 2,this,1);
            grid = grid.CopyTrim();
            return grid;
        }
        internal IEnumerable<struct_path> read_paths()
        {
            foreach (var path in _read_paths()) yield return path.Move(-viewbox.x_min, -viewbox.y_min);
        }
        internal IEnumerable<struct_path> _read_paths()
        {
            struct_path p;
            var queue = new Queue<struct_path>();
            struct_path q0 = struct_path.Empty;
            struct_path_point p0 = new struct_path_point { };
            int endix = -1;
            for (var i = 0; i< length; i++)
            {
                p = paths[i];
                if (p.method == enum_path_method.Move)
                {
                    q0 = paths[i + 1];
                    var qend = struct_path.Empty;
                    for (endix = i+1; endix < length; endix++) if (paths[endix].method == enum_path_method.Move) break;
                    endix--;

                    qend = paths[endix];

                    p0 = q0.p0;
                    if (qend.method == enum_path_method.Quadratic_Bezier_Curve && q0.p0 == qend.p2)
                    {
                        if (q0.method == enum_path_method.Quadratic_Bezier_Curve)
                        {
                            q0.p0 = helper.calc_middle_point(q0.p0, q0.p1);
                            p.p1 = q0.p0;
                            yield return p;
                            p = q0;
                            i++;
                        }
                        else if (q0 .method == enum_path_method.Line)
                        {
                            q0.p0 = helper.calc_middle_point(q0.p0, q0.p1);
                            p.p1 = q0.p0;
                            yield return p;
                            p = q0;
                            i++;
                        }
                    }
                }
                else if (i == endix && p.method == enum_path_method.Quadratic_Bezier_Curve)
                {
                    if (q0.method == enum_path_method.Quadratic_Bezier_Curve)
                    {
                        p.p2 = helper.calc_middle_point(p.p1, p.p2);
                        yield return p;
                        p = new struct_path { method = enum_path_method.Quadratic_Bezier_Curve, p0 = p.p2, p1 = p0, p2 = q0.p0 };
                    }
                    else if (q0.method == enum_path_method.Line)
                    {
                        p.p2 = helper.calc_middle_point(p.p1, p.p2);
                        yield return p;
                        p = new struct_path { method = enum_path_method.Quadratic_Bezier_Curve, p0 = p.p2, p1 = p0, p2 = q0.p0 };
                    }
                }

                yield return p;

            }
        }
        internal string ToSvgTextPath()
        {
            var text = string.Empty;
            foreach (var p in read_paths())
            {
                switch (p.method)
                {
                    case enum_path_method.Move:
                        text += $"M {p.p1.x},{p.p1.y}\n";
                        break;
                    case enum_path_method.Line:
                        text += $"L {p.p1.x},{p.p1.y}\n";
                        break;
                    case enum_path_method.Quadratic_Bezier_Curve:
                        text += $"Q {p.p1.x},{p.p1.y} {p.p2.x},{p.p2.y}\n";
                        break;
                }
            }
            return text;
        }
    }
}
